/* ======================================================================================== *\

		FILE:				3DPipesInD3D10.cpp
		AUTHOR:				Jack Hoxley

		DESCRIPTION:		The main entry point for this application. This handles the
							events/callbacks raised by the DXUT framework.

		CHANGE:				4th May 2007
								Removed all references to Direct3D 9
							5th May 2007
								Integrated profiling macros

\* ======================================================================================== */

// Include Standard Headers
// ------------------------
#include "DXUT.h"




// Include project headers
// -----------------------
#include "Utils.h"
#include "Profiling.h"
#include "PipeLayout.h"
#include "ScreensaverMode.h"
#include "PipeRenderer.h"
#include "ForwardRenderer.h"
#include "InstancedForwardRenderer.h"


// Global Variables (yuk)
// ----------------------
PipeSim::PipeLayout*				g_pLayout				= NULL;

Renderer::PipeRenderer*				g_pRenderingPaths[] =
									{
										new Renderer::ForwardRenderer( ),
										new Renderer::InstancedForwardRenderer( )
									};
UINT16								g_CurrentRenderingPath = 0;


//--------------------------------------------------------------------------------------
// Reject any D3D10 devices that aren't acceptable by returning false
//--------------------------------------------------------------------------------------
bool CALLBACK IsD3D10DeviceAcceptable( UINT Adapter, UINT Output, D3D10_DRIVER_TYPE DeviceType, DXGI_FORMAT BackBufferFormat, bool bWindowed, void* pUserContext )
{
	ENTER;

    return true;
}


//--------------------------------------------------------------------------------------
// Called right before creating a D3D9 or D3D10 device, allowing the app to modify the device settings as needed
//--------------------------------------------------------------------------------------
bool CALLBACK ModifyDeviceSettings( DXUTDeviceSettings* pDeviceSettings, void* pUserContext )
{
	ENTER;

    return true;
}


//--------------------------------------------------------------------------------------
// Create any D3D10 resources that aren't dependant on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D10CreateDevice( ID3D10Device* pd3dDevice, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
	ENTER;

	// Initialize the first rendering path
	HRESULT hr = g_pRenderingPaths[g_CurrentRenderingPath]->CreateResources( pd3dDevice, g_pLayout );

	if( FAILED( hr ) )
	{
		ERR_OUT( L"Unable to create first rendering path's resources." );

		return hr;
	}
	else
	{
		// Output some simple information...
		WCHAR wcName[512];
		g_pRenderingPaths[g_CurrentRenderingPath]->RendererName( wcName, 512 );
		
		WCHAR wcDebug[768];
		StringCchPrintf( wcDebug, 768, L"Successfully created renderer called '%s' (consuming %d bytes of VRAM)\n", wcName, g_pRenderingPaths[g_CurrentRenderingPath]->VideoMemoryStorageInUse() );
		Utils::OutputMessage( Utils::Information, WIDEN( __FUNCTION__ ), __LINE__, wcDebug );
	}

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Create any D3D10 resources that depend on the back buffer
//--------------------------------------------------------------------------------------
HRESULT CALLBACK OnD3D10ResizedSwapChain( ID3D10Device* pd3dDevice, IDXGISwapChain *pSwapChain, const DXGI_SURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
{
	ENTER;

    return S_OK;
}


//--------------------------------------------------------------------------------------
// Handle updates to the scene.  This is called regardless of which D3D API is used
//--------------------------------------------------------------------------------------
void CALLBACK OnFrameMove( double fTime, float fElapsedTime, void* pUserContext )
{
	ENTER;

	static double LastSimulationUpdate = 0.0;

	if( fTime - LastSimulationUpdate > (1.0f / 30.0f) )
	{
		LastSimulationUpdate = fTime;

		g_pLayout->IterateSimulation( );
		g_pRenderingPaths[g_CurrentRenderingPath]->UpdateResources( g_pLayout );
	}
}


//--------------------------------------------------------------------------------------
// Render the scene using the D3D10 device
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D10FrameRender( ID3D10Device* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
{
	ENTER;

    // Clear render target and the depth stencil 
    float ClearColor[4] = { 0.176f, 0.196f, 0.667f, 0.0f };
    pd3dDevice->ClearRenderTargetView( DXUTGetD3D10RenderTargetView(), ClearColor );
    pd3dDevice->ClearDepthStencilView( DXUTGetD3D10DepthStencilView(), D3D10_CLEAR_DEPTH, 1.0, 0 );

	//Determine the current transformations
	D3DXVECTOR3 pEye = D3DXVECTOR3( 1.25f, 1.25f, 1.25f );
	D3DXVECTOR3 pAt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );	
	D3DXVECTOR3 pUp = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );

	D3DXMATRIX mView;
	D3DXMatrixLookAtLH( &mView, &pEye, &pAt, &pUp );

	D3DXMATRIX mProj;
	DXUTD3D10DeviceSettings setup = DXUTGetDeviceSettings().d3d10;
	float Aspect = static_cast< float >( setup.sd.BufferDesc.Width ) / static_cast< float >( setup.sd.BufferDesc.Height );
	D3DXMatrixPerspectiveFovLH( &mProj, D3DX_PI / 3.0f, Aspect, 0.5f, 5.0f );

	D3DXMATRIX mViewProj = mView * mProj;

	g_pRenderingPaths[g_CurrentRenderingPath]->Render( pd3dDevice, mViewProj );
}


//--------------------------------------------------------------------------------------
// Release D3D10 resources created in OnD3D10ResizedSwapChain 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D10ReleasingSwapChain( void* pUserContext )
{
	ENTER;
}


//--------------------------------------------------------------------------------------
// Release D3D10 resources created in OnD3D10CreateDevice 
//--------------------------------------------------------------------------------------
void CALLBACK OnD3D10DestroyDevice( void* pUserContext )
{
	ENTER;

	if( NULL != g_pRenderingPaths[g_CurrentRenderingPath] )
	{
		if( FAILED( g_pRenderingPaths[g_CurrentRenderingPath]->DestroyResources( ) ) )
		{
			ERR_OUT( L"Failed to release current rendering path's resources." );
		}
	}
}


//--------------------------------------------------------------------------------------
// Handle messages to the application
//--------------------------------------------------------------------------------------
LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, 
                          bool* pbNoFurtherProcessing, void* pUserContext )
{
	ENTER;

    return 0;
}


//--------------------------------------------------------------------------------------
// Handle key presses
//--------------------------------------------------------------------------------------
void CALLBACK OnKeyboard( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
{
	ENTER;

#ifdef SCREENSAVER
	if( ScrnSv::RunAsStandardScreensaver() )
	{
		// We're running as a fullscren screensaver, so if we press any
		// keys then we should exit the application
		INFO_OUT( L"Key event causes screensaver to successfully finish execution." );
		DXUTShutdown( 0 );
	}
#endif
}


//--------------------------------------------------------------------------------------
// Handle mouse button presses
//--------------------------------------------------------------------------------------
void CALLBACK OnMouse( bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown, 
                       bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta, 
                       int xPos, int yPos, void* pUserContext )
{
	ENTER;

#ifdef SCREENSAVER
	if( ScrnSv::RunAsStandardScreensaver() )
	{
		// We want to add a slight delay so that movement events immediately as
		// the screensaver launches don't cause it to immediately close. Mostly only
		// occurs during testing or when previewing from the control panel..
		if( DXUTGetTime() > 1.0 )
		{
			INFO_OUT( L"Mouse-move event causes screensaver to successfully finish execution." );
			DXUTShutdown( 0 );
		}
	}
#endif
}


//--------------------------------------------------------------------------------------
// Call if device was removed.  Return true to find a new device, false to quit
//--------------------------------------------------------------------------------------
bool CALLBACK OnDeviceRemoved( void* pUserContext )
{
	ENTER;

    return true;
}


//--------------------------------------------------------------------------------------
// Initialize everything and go into a render loop
//--------------------------------------------------------------------------------------
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
	//_CrtSetBreakAlloc( # );
#endif

    // Set general DXUT callbacks
    DXUTSetCallbackFrameMove( OnFrameMove );
    DXUTSetCallbackKeyboard( OnKeyboard );
#ifdef SCREENSAVER
	// If in screensaver mode we want the user to be able
	// to move the mouse and trigger the application to close
	DXUTSetCallbackMouse( OnMouse, true );
#else
    DXUTSetCallbackMouse( OnMouse );
#endif
    DXUTSetCallbackMsgProc( MsgProc );
    DXUTSetCallbackDeviceChanging( ModifyDeviceSettings );
    DXUTSetCallbackDeviceRemoved( OnDeviceRemoved );

    // Set the D3D10 DXUT callbacks.
    DXUTSetCallbackD3D10DeviceAcceptable( IsD3D10DeviceAcceptable );
    DXUTSetCallbackD3D10DeviceCreated( OnD3D10CreateDevice );
    DXUTSetCallbackD3D10SwapChainResized( OnD3D10ResizedSwapChain );
    DXUTSetCallbackD3D10FrameRender( OnD3D10FrameRender );
    DXUTSetCallbackD3D10SwapChainReleasing( OnD3D10ReleasingSwapChain );
    DXUTSetCallbackD3D10DeviceDestroyed( OnD3D10DestroyDevice );

    // Perform any application-level initialization here
	bool bStartInFullscreenMode = false;

#ifdef SCREENSAVER
	// We're in screensaver mode, decode the command line to
	// determine how we should launch the application.
	ScrnSv::ParseApplicationCommandLine( );

	if( ScrnSv::ShowConfigurationDialog() )
	{
		// If this function returned true then the dialog was
		// shown, which implies that the command line argument
		// doesn't actually want the full screensaver to execute.
		INFO_OUT( L"OS requested application to show screensaver configuration dialog. Successfully exiting application WITHOUT running full screensaver." );
		return 0;
	}

	bStartInFullscreenMode = ScrnSv::RunAsStandardScreensaver( );
#endif

	srand( GetTickCount() );
	g_pLayout = new PipeSim::PipeLayout( 10, 10, 10, 1 );

    DXUTInit( true, true, NULL ); // Parse the command line, show msgboxes on error, no extra command line params
    DXUTSetCursorSettings( true, true ); // Show the cursor and clip it when in full screen
#ifdef SCREENSAVER
	// If we're in preview mode we tell DXUT to use the window
	// handle provided to us by the OS...
	HWND hPreviewWindow = ScrnSv::RunAsPreviewScreensaver( );

	if( 0 == hPreviewWindow )
	{
		// Not in preview mode!
		DXUTCreateWindow( L"'3D Pipes' in Direct3D 10" );
	}
	else
	{
		// Preview mode enabled!
		DXUTSetWindow( hPreviewWindow, hPreviewWindow, hPreviewWindow );
	}
#else
    DXUTCreateWindow( L"'3D Pipes' in Direct3D 10" );
#endif
    DXUTCreateDevice( !bStartInFullscreenMode, 640, 480 );  

	// Profiling code relies on the device being created,
	// so we have to execute this code between CreateDevice() and MainLoop()
	#ifdef PROFILE
		// Enable the use of the ID3D10Debug interface for synchronization
		D3DUtils::RequestGPUSynchronization( );

		// Set up the ID3D10InfoQueue to capture application messages.
		D3DUtils::InitializeTracing( );
	#endif

    DXUTMainLoop(); // Enter into the DXUT render loop

    // Perform any application-level cleanup here
	SAFE_DELETE( g_pLayout );

	size_t RenderingPathCount = sizeof( g_pRenderingPaths ) / sizeof( Renderer::PipeRenderer* );
	for( size_t i = 0; i < RenderingPathCount; ++i )
	{
		g_pRenderingPaths[i]->DestroyResources( );
		SAFE_DELETE( g_pRenderingPaths[i] );
	}

	#ifdef PROFILE
		// Need to ensure that any captured information gets
		// saved to disk
		D3DUtils::TerminateTracing( );
	#endif

    return DXUTGetExitCode();
}


